#region Using Systems

using System;
using System.Collections.Generic;
using HumanElement.Xna.Core;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

#endregion

namespace HumanElement.Xna
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class CameraServices : DrawableGameComponent, ICameraServices
    {
        private Dictionary<object, ICamera> cameras;
        private CameraContext context;

        /// <summary>
        /// Initializes a new instance of the <see cref="CameraServices"/> class.
        /// </summary>
        /// <param name="game">The game.</param>
        /// <param name="camera">The camera.</param>
        public CameraServices(Game game, ICamera camera) : this(game)
        {
            Current = camera;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CameraServices"/> class.
        /// </summary>
        /// <param name="game">Game that the game component should be attached to.</param>
        public CameraServices(Game game) : base(game)
        {
            if (Current == null)
            {
                Current = new Camera(game as IGameEngine, "DefaultCamera");
                Current.Initialize();
            }
        }

        /// <summary>
        /// Gets the viewport.
        /// </summary>
        /// <value>The viewport.</value>
        protected Viewport Viewport
        {
            get { return GraphicsDevice.Viewport; }
        }

        /// <summary>
        /// Gets the engine.
        /// </summary>
        /// <value>The engine.</value>
        protected IGameEngine Engine
        {
            get { return base.Game as IGameEngine; }
        }

        #region ICameraServices Members

        /// <summary>
        /// Gets the current TEntity for the Game Service.
        /// </summary>
        /// <value>The current entity.</value>
        public ICamera Current { get; private set; }

        /// <summary>
        /// Gets or sets the name.
        /// </summary
        /// <value>The name.</value>
        public string Name { get; set; }

        /// <summary>
        /// Gets the specified key.
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public ICamera Get<TKey>(TKey key)
        {
            if (cameras == null)
            {
                cameras = new Dictionary<object, ICamera>();
                return null;
            }
            else if (cameras.ContainsKey(key))
            {
                return cameras[key];
            }
            else
            {
                throw new KeyNotFoundException(string.Format("The camera {0} was not found.", key));
            }
        }

        /// <summary>
        /// Gets the supported entity.
        /// </summary>
        /// <returns></returns>
        public Type GetSupportedEntity()
        {
            return typeof (ICamera);
        }

        /// <summary>
        /// Initializes the specified engine.
        /// </summary>
        public override void Initialize()
        {
            context = new CameraContext
                          {
                              AspectRatio = Viewport.AspectRatio,
                              FieldOfView = MathHelper.PiOver4,
                              NearClippingPlane = 1.0f,
                              FarClippingPlane = 1000.0f,
                              GraphicsDevice = GraphicsDevice
                          };
        }

        /// <summary>
        /// Sets the active camera.
        /// </summary>
        /// <param name="camera">The camera.</param>
        /// <returns></returns>
        public bool SetActiveCamera(ICamera camera)
        {
            bool res = false;

            if (Current != camera)
            {
                Current = camera;
                Current.Active = true;
                Current.Translate();

                res = true;
            }

            return res;
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            context.ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(context.FieldOfView, context.AspectRatio,
                                                                           context.NearClippingPlane,
                                                                           context.FarClippingPlane);
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            Current.RenderFrame(context);
        }

        #endregion
    }
}